package com.funny.stock.trade.service;

import com.funny.stock.base.redis.RedisTool;
import com.funny.stock.base.service.BaseCrudService;
import com.funny.stock.base.util.PageUtil;
import com.funny.stock.base.util.PropertiesCopyUtil;
import com.funny.stock.base.vo.TradeType;
import com.funny.stock.data.vo.RedisKeyUtil;
import com.funny.stock.data.vo.StockConstants;
import com.funny.stock.trade.dao.pojo.DayTradeProfitDO;
import com.funny.stock.trade.dao.pojo.DayTradeRecordDO;
import com.funny.stock.trade.dao.repo.DayTradeRecordRepo;
import com.funny.stock.trade.vo.ComposeTradeProfitVO;
import com.funny.stock.trade.vo.TradeActionInfo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.persistence.criteria.Predicate;

/**
 * @Description  
 * @Author  pengl
 * @Date 2020-11-18
 */

@Service
public class DayTradeRecordService extends BaseCrudService<DayTradeRecordDO,Long> {

	@Autowired
	DayTradeRecordRepo repository;

	@Autowired
	GridTradePlanVarService gridTradePlanVarService;

	@Autowired
	DayTradeProfitService dayTradeProfitService;

	@Autowired
	RedisTool redisTool;

	protected JpaRepository<DayTradeRecordDO, Long> setupJpaRepository() {
		return repository;
	}

//	public Page<DayTradeRecordDO> findByComposeIdAndPlanIdAndPlanType(Long composeId,Long planId, String planType, PageRequest request){
//		return repository.findByComposeIdAndPlanIdAndPlanType(composeId,planId, planType, request);
//	}
//
//	public List<DayTradeRecordDO> findByComposeIdAndPlanIdAndPlanTypeAndType(Long composeId,Long planId, String planType,Integer type){
//		return repository.findByComposeIdAndPlanIdAndPlanTypeAndType(composeId,planId, planType, type);
//	}
//
//	public Page<DayTradeRecordDO> findByPlanIdAndStockCodeAndPlanType(Long composeId,Long planId, String code, String planType, PageRequest request){
//		return repository.findByComposeIdAndPlanIdAndStockCodeAndPlanType(composeId,planId, code, planType, request);
//	}

	@Transactional
	public DayTradeProfitDO doTradeAction(TradeActionInfo tradeInfo){

		DayTradeRecordDO recordDO = new DayTradeRecordDO();

		recordDO.setType(tradeInfo.getTradeType().getType());
		recordDO.setCreateTime(new Date());
		recordDO.setTradeTime(recordDO.getCreateTime());
		recordDO.setPlanId(tradeInfo.getPlanId());
		recordDO.setPlanType(tradeInfo.getPlanType());
		recordDO.setComposeId(tradeInfo.getComposeId());
		recordDO.setStockCode(tradeInfo.getRealtimeStockData().getSymbol());
		recordDO.setStockName(tradeInfo.getRealtimeStockData().getName());
		BigDecimal price = BigDecimal.valueOf(tradeInfo.getRealtimeStockData().getPrice());
		recordDO.setTotalMoney(price.multiply(BigDecimal.valueOf(tradeInfo.getTradeNumber())));
		recordDO.setTradeNum(tradeInfo.getTradeNumber());
		recordDO.setTradePrice(price);
		recordDO.setUserId(tradeInfo.getPersonId());
		recordDO.setIsMatch(0);

		//进行套利配对，查找所有没有匹配的其他交易类型
		boolean isCompleteMatch = false;
		List<DayTradeRecordDO> saveRecordDOS = null;

		String composeProfitKey = getComposeProfitKey(tradeInfo.getPersonId(), tradeInfo.getComposeId());
		ComposeTradeProfitVO profitVO = (ComposeTradeProfitVO) redisTool.hGet(composeProfitKey, tradeInfo.getCode());
		//更新获利信息
		if(profitVO == null){
			//数据库查找
			profitVO = queryComposeTradeProfitVO(recordDO);
		}

		DayTradeRecordDO queryRecordDO = PropertiesCopyUtil.copyProperties(recordDO,DayTradeRecordDO.class);
		queryRecordDO.setType(tradeInfo.getTradeType() == TradeType.BUY ? TradeType.SELL.getType() : TradeType.BUY.getType());
		//查找对应买入或卖出的最近一条记录进行匹配
		Page<DayTradeRecordDO> dayTradeRecordDOS = queryByCondition(queryRecordDO, 0, 1);

		List<DayTradeRecordDO> content = dayTradeRecordDOS.getContent();
		if(!CollectionUtils.isEmpty(content)){
			recordDO.setIsMatch(1);

			DayTradeRecordDO anotherRecord = content.get(0);
			anotherRecord.setIsMatch(1);

			isCompleteMatch = true;
			List<DayTradeRecordDO> recordDOS = Arrays.asList(recordDO, anotherRecord);
			saveRecordDOS = repository.saveAll(recordDOS);
		}else{
			repository.save(recordDO);
		}

		if(tradeInfo.getTradeType() == TradeType.BUY) {
			profitVO.setBuyNumber(profitVO.getBuyNumber() + 1);
		}else{
			profitVO.setSellNumber(profitVO.getBuyNumber() + 1);
		}

		DayTradeProfitDO profitDO = null;
		if(isCompleteMatch){
			if(saveRecordDOS.size() < 2) return null;
			Long buyId,sellId;
			BigDecimal profit = BigDecimal.ZERO;
			if(saveRecordDOS.get(0).getType() == TradeType.BUY.getType()){
				buyId = saveRecordDOS.get(0).getId();
				sellId = saveRecordDOS.get(1).getId();
				profit = saveRecordDOS.get(1).getTotalMoney().subtract(saveRecordDOS.get(0).getTotalMoney());
			}else{
				buyId = saveRecordDOS.get(1).getId();
				sellId = saveRecordDOS.get(0).getId();
				profit = saveRecordDOS.get(0).getTotalMoney().subtract(saveRecordDOS.get(1).getTotalMoney());
			}
			//保存获利记录
			DayTradeProfitDO dayTradeProfitDO = new DayTradeProfitDO();
			dayTradeProfitDO.setComposeId(tradeInfo.getComposeId());
			dayTradeProfitDO.setStockCode(tradeInfo.getCode());
			dayTradeProfitDO.setCreateId(tradeInfo.getPersonId());
			dayTradeProfitDO.setBuyTradeId(buyId);
			dayTradeProfitDO.setSellTradeId(sellId);
			dayTradeProfitDO.setPlanType(tradeInfo.getPlanType());
			dayTradeProfitDO.setPlanId(tradeInfo.getPlanId());
			dayTradeProfitDO.setProfit(profit);
			dayTradeProfitDO.setDate(new Date());
			profitDO = dayTradeProfitService.save(dayTradeProfitDO);

			profitVO.setProfitNumber(profitVO.getProfitNumber() + 1);
			profitVO.setProfitMoney(profitVO.getProfitMoney().add(profit));
			redisTool.hPut(composeProfitKey, tradeInfo.getPlanId().toString(), profitVO);
			redisTool.expire(composeProfitKey, StockConstants.KEY_DEFAULT_EXPIRE_HOURS, TimeUnit.HOURS);
		}
		return profitDO;
	}


	public Map<Long,ComposeTradeProfitVO> queryComposeTradeProfitMap(Long personId,Long composeId,String planType) {
		String composeProfitKey = getComposeProfitKey(personId, composeId);
		Map entries = redisTool.hGetAll(composeProfitKey);
		if(CollectionUtils.isEmpty(entries)){
			List<DayTradeProfitDO> profitList = dayTradeProfitService.findTradeProfitList(composeId, planType, personId);

			if(!CollectionUtils.isEmpty(profitList)) {
				Map<Long, DayTradeProfitDO> profitDOMap = profitList.stream().collect(Collectors.toMap(DayTradeProfitDO::getPlanId, Function.identity(), (k1, k2) -> k2));
				entries = profitDOMap;
				redisTool.hPutAll(composeProfitKey, profitDOMap);
				redisTool.expire(composeProfitKey, StockConstants.KEY_DEFAULT_EXPIRE_HOURS, TimeUnit.HOURS);
			}
		}
		return entries;
	}

	private ComposeTradeProfitVO queryComposeTradeProfitVO(DayTradeRecordDO recordDO) {
		ComposeTradeProfitVO profitVO;
		long buyCount = getTradeTypeCount(recordDO, TradeType.BUY);
		long sellCount = getTradeTypeCount(recordDO, TradeType.SELL);
		profitVO = new ComposeTradeProfitVO();
		profitVO.setStockCode(recordDO.getStockCode());
		profitVO.setStockName(recordDO.getStockName());
		profitVO.setPlanId(recordDO.getPlanId());
		profitVO.setPlanType(recordDO.getPlanType());
		profitVO.setComposeId(recordDO.getComposeId());
		profitVO.setCreateId(recordDO.getUserId());

		profitVO.setSellNumber(sellCount);
		profitVO.setBuyNumber(buyCount);

		List<DayTradeProfitDO> list = dayTradeProfitService.findByParams(recordDO.getComposeId(), recordDO.getPlanId(),
				recordDO.getPlanType(), recordDO.getStockCode(), recordDO.getUserId());
		profitVO.setProfitNumber((long) list.size());
		BigDecimal sum = BigDecimal.ZERO;
		for (DayTradeProfitDO item : list) {
			sum = sum.add(item.getProfit());
		}
		profitVO.setProfitMoney(sum);
		return profitVO;
	}

	public long getTradeTypeCount(DayTradeRecordDO recordDO, TradeType tradeType){
		Page<DayTradeRecordDO> byTradeType = repository.findByTradeType(recordDO.getComposeId(), recordDO.getPlanId(),
				recordDO.getPlanType(), recordDO.getStockCode(), tradeType.getType(), PageUtil.buildPageRequest(1,Integer.MAX_VALUE));
		return byTradeType.getTotalElements();
	}

	public Page<DayTradeRecordDO> queryByCondition(DayTradeRecordDO recordDO,Integer pageNumber,Integer pageSize){
		Specification specification = (Specification) (root, criteriaQuery, criteriaBuilder) -> {
			List<Predicate> predicates = new ArrayList<>();

			if(!StringUtils.isEmpty(recordDO.getStockCode())) {
				predicates.add(criteriaBuilder
						.equal(root.get("stockCode"), recordDO.getStockCode()));
			}

			if(null != recordDO.getUserId()){
				predicates.add(criteriaBuilder.equal(root.get("userId"), recordDO.getUserId()));
			}

			if(null != recordDO.getComposeId()){
				predicates.add(criteriaBuilder.equal(root.get("composeId"), recordDO.getComposeId()));
			}

			if(null != recordDO.getPlanId()){
				predicates.add(criteriaBuilder.equal(root.get("planId"), recordDO.getPlanId()));
			}

			if(null != recordDO.getType()){
				predicates.add(criteriaBuilder.equal(root.get("type"), recordDO.getType()));
			}

			if(null != recordDO.getIsMatch()) {
				predicates.add(criteriaBuilder.equal(root.get("isMatch"), recordDO.getIsMatch()));
			}

			return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
		};
		PageRequest pageRequest = PageRequest.of(pageNumber, pageSize, Sort.Direction.DESC, "createTime");
		return repository.findAll(specification,pageRequest);
	}


	private String getComposeProfitKey(Long createId,Long composeId){
		return RedisKeyUtil.getComposeProfitKey(createId, composeId);
	}


}
